#pragma once
#include <list>
#include <memory>
#include <unordered_map>

#include "buffer/lru_replacer.h"
#include "buffer/page_allocator.h"
#include "storage/disk_manager.h"
#include "storage/disk_scheduler.h"
#include "storage/page.h"
#include "storage/page_guard.h"

namespace boltDB {
class BufferPoolManager {
 public:
  /*
    @breif 构造函数，构造一个缓冲池管理器
    @param pool_size    缓冲池可以缓冲的页面数量
    @param allocator    页面分配器，用于控制页面在DB文件中的分配情况
    @param disk_manager 磁盘管理器，用于控制在磁盘上读写页面
    @param replacer_k   lru-k 算法的 k 的值
 */
  BufferPoolManager(size_t pool_size, BasicPageAllocator *allocator,
                    DiskManager *disk_manager,
                    size_t replacer_k = LRUK_REPLACER_K);

  ~BufferPoolManager();

  auto GetPoolSize() -> size_t { return pool_size_; }
  /** 获取缓冲区内的页面*/
  auto GetPages() -> Page * { return pages_; }
  /* 
    @breif 获取一个新页面
    @param page_id  新页面的页面id会被写入到page_id中
    @return 如果分配成功 会返回一个带有 BOLTDB_PAGE_SIZE大小缓冲区的页面
            否则 返回空指针 nullptr;
  */
  auto NewPage(page_id_t *page_id) -> Page *;

  auto NewPageGuarded(page_id_t *page_id) -> BasicPageGuard;
  
  auto FetchPage(page_id_t page_id) -> Page *;
  /*
    获取页面加锁版本的RAII封装类
    但其实这里有个隐患，page可以直接向外传递data指针来破环这种封装
  */
  auto FetchPageBasic(page_id_t page_id) -> BasicPageGuard;
  auto FetchPageRead(page_id_t page_id) -> ReadPageGuard;
  auto FetchPageWrite(page_id_t page_id) -> WritePageGuard;
  /*
    @breif 解除对页面的引用，并设置页面是否经过修改
    @param page_id 要解除引用的页面id
    @param is_dirty 页面是否经过修改
  */
  auto UnRefPage(page_id_t page_id, bool is_dirty) -> bool;

  auto FlushPage(page_id_t page_id) -> bool;

  void FlushAllPages();

  auto DeletePage(page_id_t page_id) -> bool;

 private:
  /** frame_id_t 就是上就是个索引，这里是为了与page_id做区分*/
  const size_t pool_size_;
  /** 被缓冲的页，实际上这是个数组 frame_id 就是用来访问数据元素的 */
  Page *pages_;
  /** 磁盘管理器 */
  std::unique_ptr<DiskScheduler> disk_scheduler_;
  /** 记录被缓冲的页面ID 与缓冲池内的frame_id对应关系 */
  std::unordered_map<page_id_t, frame_id_t> page_table_;
  /** 替换策略，用于控制是否将页面换出内存，还是从磁盘中读取页面 */
  std::unique_ptr<LRUKReplacer> replacer_;
  /** 空闲链表，在此的帧都是没有对应的页面的，也就是可以被分配页面加入缓冲池中
   */
  std::list<frame_id_t> free_list_;
  /** 
    页面分配器，控制新页面的页面id的分配
    由std::unique_ptr<BasicPageAllocator> 变更为 ——> BasicPageAllocator*
    理由是 page_allocator的生命周期应该与 buffer_pool_manager保持一致，
    但是由于后期会加入事务，而事务结束时，freelists应该立刻回写至磁盘，防止出现数据不一致的情况，
    因此 page_allocator 的生命周期 不应该由bufferpool控制同样由db.close()进行控制
  */
  BasicPageAllocator* page_allocator_;
  /** 互斥锁保证并发安全*/
  std::mutex latch_;
};
}  // namespace boltDB